#ifndef DISTANCE_H
#define DISTANCE_H
#include <boost/numeric/ublas/vector.hpp>
#include "Trajectory.h"
#include <math.h>
#define M_PI 3.14159265358979323846 

namespace Metrics{

	//  
	// Euclidean distance
	template 
	<typename VEC_T>	
	class Euclidean {
	
	protected:                  

		typedef typename VEC_T vector_type;

		// this must be not directly accessible 
		// since we want to provide a rich set of distances	

		long double getDistance(const VEC_T v1, const VEC_T v2)
		{
			return norm_2(v1-v2);
		};
	};
	
	template 
	<typename VEC_T>	
	class Cosine {
	
	protected:                  

		typedef typename VEC_T vector_type;

								// this must be not directly accessible 
								// since we want to provide a rich set of distances	

		long double getDistance(const VEC_T v1, const VEC_T v2)
		{
			return 1.0 - (inner_prod(v1, v2) / (norm_2(v1) * norm_2(v2)));
		};

		long double getSimilarity(const VEC_T v1, const VEC_T v2)
		{
		//	std::cout << "dot=" << prec_inner_prod(v1, v2) << " norm_2=" << norm_2(v1) << "norm2=" << norm_2(v2) << std::endl;
			return prec_inner_prod(v1, v2) / (norm_2(v1) * norm_2(v2));
		};
	};


	/*
	Our distance calculation !!
	*/

	template 
	<typename VEC_T>	
	class MeterDistance {
	
	protected:                  

		typedef typename VEC_T vector_type;

								// this must be not directly accessible 
								// since we want to provide a rich set of distances	
		//we don't use it...
		long double getDistance(const VEC_T v1, const VEC_T v2)
		{
			
			
			PointTraj p1 = (v1[0], v1[1], 0, v1[2]);
			PointTraj p2 = (v2[0], v2[1], 0, v2[2]);

			long double geographicDistance = Trajectory::distance2Points(p1, p2);

			//difference between the 2 timestamp in second
			long double timeDiff = fabs(p2.getTimestamp() - p1.getTimestamp()) * 24 * 3600;

			cout << "geo dist : " << geographicDistance << endl;
			cout << "time Diff : " << timeDiff << endl;

			return 1.0 - (inner_prod(v1, v2) / (norm_2(v1) * norm_2(v2)));
		};

		long double getSimilarity(const VEC_T v1, const VEC_T v2, long double thresholdGeo)
		{

			
			
			PointTraj p1 = PointTraj(v1[0], v1[1], 0, v1[2]);
			PointTraj p2 = PointTraj(v2[0], v2[1], 0, v2[2]);

			//double geographicDistance = Trajectory::distance2Points(p1, p2);

			// to get the length or the arc: distArc = angle*R*Pi/180
			long double a = M_PI/180;

			//angle difference * earth radius in m
			long double distLat = (p2.getLatitude() - p1.getLatitude())*6371000*a;
			long double distLongi = (p2.getLongitude() - p1.getLongitude())*6371000*a;


			//distance between the 2 points
			long double geographicDistance = sqrt(distLongi*distLongi + distLat*distLat);


		//	std::cout << "dot=" << prec_inner_prod(v1, v2) << " norm_2=" << norm_2(v1) << "norm2=" << norm_2(v2) << std::endl;
			return thresholdGeo / geographicDistance;
		};


		long double getSimilarityTime(const VEC_T v1, const VEC_T v2, long double thresholdTime){
			
			PointTraj p1 = PointTraj(v1[0], v1[1], 0, v1[2]);
			PointTraj p2 = PointTraj(v2[0], v2[1], 0, v2[2]);

			//difference between the 2 timestamp in second
			long double timeDiff = fabs(p2.getTimestamp() - p1.getTimestamp()) * 24 * 3600;
			
			return thresholdTime / timeDiff;
		}
	};






	template
	<typename Distance_Policy>   // this allows to provide a static mechanism for pseudo-like 
								 // inheritance, which is optimal from a performance point of view.
	class Distance : Distance_Policy
	{
		using Distance_Policy::getDistance;

	public:
		long double distance(typename Distance_Policy::vector_type x, typename Distance_Policy::vector_type y)
		{
			return getDistance(x, y);
		};

		long double similarity(typename Distance_Policy::vector_type x, typename Distance_Policy::vector_type y, long double thresholdGeo)
		{
			
			return getSimilarity(x, y, thresholdGeo);
		};

		long double similarityTime(typename Distance_Policy::vector_type x, typename Distance_Policy::vector_type y, long double thresholdTime)
		{
			return getSimilarityTime(x, y, thresholdTime);
		};

	};
}

#endif